
/**
 * 时间轴
 */
export class useTimeLine {
    /**
     * 基时间
     */
    baseDate: Date | null
    /**
     * 时间数组
     */
    DateArray: string[] = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]
    /**
     * 记录单位的数组 ["号","周","月","年"]
     */
    UnitArray: string[] = ["号", "周", "年", "年"]
    /**
     * 记录是否是初次加载 默认：是
     */
    isInitLoad:boolean = true;
    /**
     * 当前显示时间轴的级别,默认：1
     */
    TimeLevel: number = 1;
    /**
     * 最外层的元素元素
     */
    ParentNode: HTMLElement | null;
    /**
     * 需要显示未来几天的数据
     */
    ReviewNum: number = 10;
    /**
     * 每个时间段需要的长度
     */
    TimeSlotWidth: number = 0
    /**
     * 每个时间段又分出多少间隔，默认：24
     */
    TimeSlotInterval: number = 60;
    /**
     * 时间段中每个间隔的长度
     */
    TimeSlotIntervalWidth: number = 0;
    /**
     * 保存当前时间进度
     */
    CurrentTimeProgress: number = 0;
    /**
     * 时间轴移动的速度
     */
    TimeMoveSpeed: number = 200;
    /**
     * 保存当前的移动进度
     */
    CurrentDay: number = 0;
    /**
     * 要显示的宏观文本数组--就是时间轴中最高级的显示单位
     */
    MacroTextArray: string[] = []
    /**
     * 要显示的微观数组
     */
    DateTextArray: number[] = []
    /**
     * 时间轴与鼠标交互时的元素
     */
    time_progressbar_tooltip: Element | null = null;
    /**
     * 覆盖在时间轴元素上方
     */
    t_progressbar: Element | null = null;
    /**
     * 自动播放按钮的点击状态回调
     * @param {*} flag  点击状态 播放：true 停止：false
     * @param {*} target 当前的元素
     */
    autoPlayClickStatus: Function | null = null;
    /**
     * @description tooltip更换显示dom
     * @description tip:请返回string形式的dom
     * @param {object} e macro:最宏观的显示单位 day:中等显示单位 date:最小显示单位
     * 
     */
    setTooltipElementFn: Function | null = null;
    /**
     * 自定义时间轴底部的显示内容
     * @param {object} macro 当前的宏远显示单位
     * @param {object} secondary 当前二级显示单位
     */
    CustomReMindLabelfn: Function | null = null;
    /**
     * 生成时间轴
     * @param eleId 最外层的id 
     * @param reviewNum 要显示的天数
     * @param level 可选参数- 显示的层级
     * @returns 
     */
    constructor(eleId: string, param?:any) {
        this.ParentNode = this.getById(eleId);
        
        if(!this.ParentNode) return this;
        
        this.ReviewNum = param.reviewNum ? param.reviewNum : 0;

        (param.CustomReMindLabelfn instanceof Function ) && (this.CustomReMindLabelfn = param.CustomReMindLabelfn);

        (param.level != void 0) && (this.TimeLevel = param.level);

        this.baseDate = param.baseDate || new Date()
        // 根据层级编辑展示信息数组
        this.useTimeShowTextByLevel()

        this.init();

        return this;
    }
    init() {

        // 如果不是第一次加载--先销毁先前添加的事件
        !this.isInitLoad && this.DestoryTimeLine()
        // 往父元素中添加内容
        this.ParentNode?.appendChild(this.createElement())
        // 添加时间轴底部的信息展示框
        this.MakeReMindElement()
        // 添加鼠标事件
        this.addEventMouse()
        // 开关添加点击事件
        this.SwitchAddClick();
        
        this.ParentNode && this.ElementAddProxy(this.ParentNode)
        
        // 改变是否初次加载的值，变为false
        this.isInitLoad = false;
    }
    /**
     * 按照显示级别来给时间轴设置显示文本
     */
    useTimeShowTextByLevel(level?: number) {
        let l = level ? level : this.TimeLevel;
        
        switch (l) {
            case 0:
                this.setDateTextArrayZore()
                // 一个小时有60分钟
                this.TimeSlotInterval = 60;
                break;
            case 1:
                this.setDateTextArrayOne()
                // 一天有24小时
                this.TimeSlotInterval = 24;
                break;
            case 2:
                this.setDateTextArrayTwo()
                // 一月有30天，但是每个月有特殊性
                this.TimeSlotInterval = 30;
                break;
            case 3:
                this.setDateTextArrayThree()
                // 一月有30天，但是每个月有特殊性
                this.TimeSlotInterval = 12;
                break;
            default:
                throw new Error("当前显示设置级别不合理")
                break;
        }
    }
    ElementAddProxy(target: HTMLElement) {
        if (!target) return false;
        let self = this;
        // let observer = new MutationObserver((mutationList) => {
        // 	observer.disconnect()
        // 	console.log("html元素尺寸发生变化：", mutationList)
        //     self.init()
        // })
        // observer.observe(target, { attributes: true, subtree:true,characterData:true, attributeFilter: ['style'], attributeOldValue: true })
        // // 观察目标节点的变化
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                if (mutation.attributeName === 'style') {
                    observer.disconnect()
                    self.init()
                }
            });
        });
        const config = {
            attributes: true,  //监听元素的属性有没有变化
            attributeFilter: ['style'],
            characterData: true,
        };
        observer.observe(target, config);
    }
    /**
     * @description 鼠标是否可以拖动时间轴的条件
     * @description true:允许
     * @description false:禁止
     */
    TimeMouseDownFlag:boolean=false;
    // 为时间轴添加鼠标相应的事件
    addEventMouse() {
        let time_progressbar = this.getByClass("time_progressbar")
        if(!time_progressbar) return false;
        // 因为使用addEventListener来绑定事件，所以使用bind来更改this的指向
        time_progressbar[0].addEventListener("mousedown",this.Time_MouseDown.bind(this))
        window.addEventListener("mousemove",this.WindowMouseMove_time.bind(this))
        window.addEventListener("mouseup",this.WindowMouseUp_time.bind(this))
        return true;
    }
    /**
     * window上的鼠标移动事件
     */
    WindowMouseMove_time(e:any){
        let self = this;
        if (!self.TimeMouseDownFlag) return false;
        self.updataElementStatus(e.offsetX)
    }
    /**
     * window上的鼠标松开事件
     */
    WindowMouseUp_time(e:any){
        this.TimeMouseDownFlag = false;
    }
    /**
     * 时间轴上的鼠标按下事件
     */
    Time_MouseDown(e:any){
        let self = this;
        self.TimeMouseDownFlag = true;
        self.updataElementStatus(e.offsetX)
    }
    /**
     * 销毁时间线中实例和绑定的事件
     */
    DestoryTimeLine(){
        let time_progressbar = this.getByClass("time_progressbar")
        if(!time_progressbar || time_progressbar?.length == 0) return false;
        time_progressbar[0].removeEventListener("mousedown",this.Time_MouseDown)
        window.removeEventListener("mousemove",this.WindowMouseMove_time)
        window.removeEventListener("mouseup",this.WindowMouseUp_time)
        // 清空父元素
        this.ParentNode && (this.ParentNode.innerHTML = "");
    }
    // 给时间轴的开关添加点击事件
    SwitchAddClick() {
        let self = this;
        let time_switch = this.getByClass("time_switch")[0] as HTMLElement;
        if (!time_switch) {
            setTimeout(() => {
                self.SwitchAddClick()
            }, 1000)
            return false;
        }
        if (time_switch == null) return false;
        let autoClickStatus = true
        time_switch.onclick = function () {
            self.autoUpdateTimeStatus(autoClickStatus);
            self.autoPlayClickStatus instanceof Function && self.autoPlayClickStatus(autoClickStatus, time_switch)
            autoClickStatus = !autoClickStatus;
        }
        return true;
    }
    // 自动播放的定时器id，方便清除定时器
    _setInterval: any;
    /**
     * 自动修改时间轴状态
     * @param flag 
     */
    autoUpdateTimeStatus(flag: boolean) {
        let self = this;

        if (flag) {

            // 某个时间段内的进一步的值
            let IntervalWidth = this.TimeSlotIntervalWidth;
            if (this.TimeLevel == 2) {
                let curr_monthDay = this.getDaysByMonth(this.CurrentDay);

                IntervalWidth = curr_monthDay / this.TimeSlotWidth;
            }
            let len = self.CurrentTimeProgress + self.TimeSlotIntervalWidth;
            let totalLen = self.TimeSlotWidth * self.ReviewNum;
            if (len >= totalLen) len = 0;

            self.updataElementStatus(len)

            self._setInterval && clearInterval(self._setInterval);
            self._setInterval = setInterval(() => {
                self.autoUpdateTimeStatus(flag);
            }, self.TimeMoveSpeed)
        } else {
            clearInterval(self._setInterval)
        }
    }
    /**
     * 修改时间轴的位置-进度
     * @param PosiX 
     * @returns 
     */
    updataElementStatus(PosiX: number) {
        if (this.t_progressbar == null || this.time_progressbar_tooltip == null) return false;
        let offsetX = PosiX;
        this.CurrentTimeProgress = offsetX;
        (this.t_progressbar as HTMLElement).style.cssText = `width:${offsetX}px;`
        this.updataShowTooltip(PosiX)
    }
    /**
     * 定时器的id，
     * 控制tip元素的消失
     */
    _setTimeoutId: any;
    /**
     * 更改tip元素样式
     * @param PosiX
     * @returns 
     */
    updataShowTooltip(PosiX: number) {

        let self = this;

        let offsetX = PosiX;

        if (this.time_progressbar_tooltip == null) return false;

        let rect = self.getDateByOffsetX(offsetX);

        this.time_progressbar_tooltip.innerHTML = (self.setTooltipElementFn instanceof Function && self.setTooltipElementFn(rect)) || `
            日期：${rect.day - 1}<br />
            时间：${rect.date}
        `

        let tooltip_rect = this.getElementRectById("time_progressbar_tooltip");

        (this.time_progressbar_tooltip as HTMLElement).style.cssText = `z-index:999;width:7%;opacity:1;left:${offsetX}px;top:-${(tooltip_rect.height) - 10}px`

        if (this._setTimeoutId != 0) clearTimeout(self._setTimeoutId);

        this._setTimeoutId = setTimeout(() => {
            clearTimeout(self._setTimeoutId)
            if (self.time_progressbar_tooltip == null) return false;
            (self.time_progressbar_tooltip as HTMLElement).style.cssText = "opacity:0,z-index:-1;"
        }, 2000)
    }
    /**
     * @description 将长度转换为时间
     * @description 本长度的起点是指时间线0点距离，不是屏幕的0点
     */
    getDateByOffsetX(len: number) {
        // 跨越的时间段
        let timeSolt = Math.floor(len / this.TimeSlotWidth);
        // 剩余的时间段距离
        let _timeSolt = Math.floor(len % this.TimeSlotWidth);

        let macro = this.MacroTextArray[Math.floor(timeSolt)];

        let day = this.DateTextArray[timeSolt] + 1;

        // 记录下当前走到哪里了
        this.CurrentDay = day;
        // 具体时间
        let date = Math.floor(_timeSolt / this.TimeSlotIntervalWidth);

        // 如果时间轴的层级是2，则因为月份会有不同，因此要使用另一种计算方法
        if (this.TimeLevel == 2) {

            let a = this.getDaysByMonth(this.DateTextArray[Math.floor(timeSolt)])
            let b = a / this.TimeSlotWidth;

            date = (Math.ceil(_timeSolt * b) % a) + 1;
        }
        return {
            macro,
            day,
            date
        }
    }
    /**
     * 添加时间提示dom
     * @returns 
     */
    MakeReMindElement() {
        let rect = this.getElementRectById("time_content");

        if (rect == null) return false;

        this.TimeSlotWidth = Math.floor(rect?.width / this.ReviewNum);

        this.TimeSlotIntervalWidth = Number((this.TimeSlotWidth / this.TimeSlotInterval).toFixed(2))

        let time_progressbar_remind = this.getByClass("time_progressbar_remind")[0];

        for (let i = 0; i < this.ReviewNum; i++) {
            let span = document.createElement("span");
            span.innerText = (this.CustomReMindLabelfn instanceof Function) ? 
                            this.CustomReMindLabelfn(this.MacroTextArray[i], this.DateTextArray[i]) : 
                            this.UnitArray[this.TimeLevel] + (Number(this.MacroTextArray[i]) + 1) + (this.DateTextArray[i] ? this.DateTextArray[i] : '')
            span.style.cssText = `width:${this.TimeSlotWidth}px;`
            time_progressbar_remind?.appendChild(span)
        }
        return true
    }
    /**
     * 设置显示的时间文本数组
     * 0级
     */
    setDateTextArrayZore() {

        let date = new Date();
        // 月份
        let month = date.getMonth() + 1;
        // 小时
        let hours = date.getHours();

        this.MacroTextArray = []
        this.DateTextArray = []

        for (let i = 0; i < this.ReviewNum; i++) {
            this.MacroTextArray.push(month + '')
            this.DateTextArray.push(hours)
            hours += 1;
            if (hours == 24) {
                month++
                hours = 0;
            }
        }

    }
    /**
     * 设置显示的时间文本数组
     * 1级
     */
    setDateTextArrayOne() {

        let date = new Date();
        // 周几
        let week = date.getDay();
        // 几号 ,因为下面会+1 因此这里要 -1
        let day = date.getDate() - 1;
        // 月份
        let month = date.getMonth() + 1;
        // 本月的天数
        let monthDay = this.getDaysByMonth(month);
        let _monthDay = 0;

        this.MacroTextArray = []
        this.DateTextArray = []

        for (let i = 0; i < this.ReviewNum; i++) {
            this.MacroTextArray.push(((week + i) % 7) + '')

            _monthDay++;
            // 避免月份出错
            if (_monthDay > monthDay) {
                month++
                monthDay = this.getDaysByMonth(month);
                _monthDay = 0;
            }
            this.DateTextArray.push(((day + i) % (monthDay)) + 1)
        }
    }
    /**
     * 设置显示的时间文本数组
     * 2级
     */
    setDateTextArrayTwo() {
        let date = new Date();
        // 月份
        let month = date.getMonth() + 1;
        // 年份
        let year = date.getFullYear();

        this.MacroTextArray = []
        this.DateTextArray = []

        for (let i = 0; i < this.ReviewNum; i++) {
            this.MacroTextArray.push(year + '')
            this.DateTextArray.push(month)
            month += 1
            if (month == 13) {
                year++
                month = 1
            }
        }
    }
    /**
     * 设置显示的时间文本数组
     * 3级
     */
    setDateTextArrayThree() {
        let date = new Date();
        // 年份
        let year = date.getFullYear();

        this.MacroTextArray = []
        this.DateTextArray = []

        for (let i = 0; i < this.ReviewNum; i++) {
            this.MacroTextArray.push(year + '')
            this.DateTextArray.push(0)
            year++
        }

    }
    /**
     * 通过月份来返回本月的时间
     * @param month 月份
     * @returns 本月的时间
     */
    getDaysByMonth(month: number) {
        let a = [1, 3, 5, 7, 8, 10, 12];
        let b = [2]
        if (a.includes(month)) {
            return 31
        } else if (b.includes(month)) {
            return this.#isLeapYear(this.baseDate?.getFullYear()) ? 29 : 28
        } else {
            return 30
        }
    }
    /**
     * 判断当前是否是闰年
     * @param year 
     * @returns 
     */
    #isLeapYear(year:number|undefined){
        if(!year) return false;
        let _year = Number(year)
        return (_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0) ? true : false
    }
    /**
     * 判断当前是否是闰年
     * @param year 
     * @returns 
     */
    isLeapYear(year:number|string){
        year = Number(year)
        return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) ? true : false
    }
    // 创建指定的元素
    createElement() {
        // 最外层的元素
        let timeLine = document.createElement("div");
        timeLine.classList.add('timeLine')
        // 控制时间轴的开关
        let time_switch = document.createElement("div");
        time_switch.classList.add('time_switch')
        // 时间轴线的外层元素
        let time_content = document.createElement("div");
        time_content.classList.add('time_content')
        // 最下面的问题提示元素--周一--周二
        let time_progressbar_remind = document.createElement("div");
        time_progressbar_remind.classList.add('time_progressbar_remind')
        // 时间轴与鼠标交互时的元素
        this.time_progressbar_tooltip = document.createElement("div");
        this.time_progressbar_tooltip.classList.add('time_progressbar_tooltip')
        // 时间轴线
        let time_progressbar = document.createElement("div");
        time_progressbar.classList.add('time_progressbar')
        // 覆盖在时间轴元素上方
        this.t_progressbar = document.createElement("div");
        this.t_progressbar.classList.add('t_progressbar')

        timeLine.appendChild(time_switch)
        timeLine.appendChild(time_content)
        time_content.appendChild(this.time_progressbar_tooltip)
        time_content.appendChild(time_progressbar)
        time_content.appendChild(time_progressbar_remind)
        time_progressbar.appendChild(this.t_progressbar)

        return timeLine;
    }
    /**
     * 通过id查找元素
     * @param id 
     * @param flag 是否往时间轴最外层元素中找元素
     * @returns 
     */
    getById(id: string,flag=true) {
        let n = flag ? document : this.ParentNode;
        return n && (n as Document).getElementById(id);
    }
    /**
     * 通过class查找元素
     * @param className 
     * @returns 
     */
    getByClass(className: string) {
        if(this.ParentNode==null) return [];
        return this.ParentNode.getElementsByClassName(className);
    }
    /**
     * 通过id获取元素的具体信息
     * @param id 
     * @returns 
     */
    getElementRectById(id: string) {
        let e = this.getByClass(id);
        return e && e[0].getBoundingClientRect();
    }
}